{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "skip"
    }
   },
   "source": [
    "<small><small><i>\n",
    "All of these python notebooks are available at [https://gitlab.erc.monash.edu.au/andrease/Python4Maths.git]\n",
    "</i></small></small>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "# Python ...\n",
    "- is an open source programming language\n",
    "- is an object-oriented programming language\n",
    "- is an interpreter-language\n",
    "- provides easy interfaces to other languages like C, Java, or Fortran\n",
    "- provides a lot of third-party modules to extend the range of functions\n",
    "- has [batteries included](https://www.python.org/dev/peps/pep-0206/)\n",
    "\n",
    "for further information: https://www.python.org/"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "## Basics\n",
    "The basic rules for writing simple statments and expressions in Python are:\n",
    "* No spaces or tab characters allowed at the start of a statement: Indentation plays a special role in Python (see the section on control statements). For now simply ensure that all statements start at the beginning of the line.\n",
    "* The '#' character indicates that the rest of the line is a comment\n",
    "* Statements finish at the end of the line:\n",
    "  * Except when there is an open bracket or paranthesis:\n",
    "```python\n",
    "1+2\n",
    "+3  #illegal continuation of the sum\n",
    "(1+2\n",
    "             + 3) # perfectly OK even with spaces\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "  * A single backslash at the end of the line can also be used to indicate that a statement is still incomplete  \n",
    "```python\n",
    "1 + \\\n",
    "   2 + 3 # this is also OK\n",
    "```\n",
    "The jupyter notebook system for writting Python intersperses text (like this) with Python statements. Try typing something into the cell (box) below and press the 'run cell' button above (triangle+line symbol) to execute it."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    }
   },
   "outputs": [],
   "source": [
    "1+2+3"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "Python has extensive help built in. You can execute [**help()**](https://docs.python.org/3/library/functions.html#help) for an overview or [**help(x)**](https://docs.python.org/3/library/functions.html#help) for any library, object or type **x** to get more information. For example:"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "source": [
    "```python\n",
    "help()\n",
    "\n",
    "Welcome to Python 3.6's help utility!\n",
    "\n",
    "If this is your first time using Python, you should definitely check out\n",
    "the tutorial on the Internet at http://docs.python.org/3.6/tutorial/.\n",
    "\n",
    "Enter the name of any module, keyword, or topic to get help on writing\n",
    "Python programs and using Python modules.  To quit this help utility and\n",
    "return to the interpreter, just type \"quit\".\n",
    "\n",
    "To get a list of available modules, keywords, symbols, or topics, type\n",
    "\"modules\", \"keywords\", \"symbols\", or \"topics\".  Each module also comes\n",
    "with a one-line summary of what it does; to list the modules whose name\n",
    "or summary contain a given string such as \"spam\", type \"modules spam\".\n",
    "\n",
    "help> \n",
    "\n",
    "You are now leaving help and returning to the Python interpreter.\n",
    "If you want to ask for help on a particular object directly from the\n",
    "interpreter, you can type \"help(object)\".  Executing \"help('string')\"\n",
    "has the same effect as typing a particular string at the help> prompt.```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "## Variables & Values\n",
    "\n",
    "A name that is used to denote something or a value is called a variable. In python, variables can be declared and values can be assigned to it as follows,"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    }
   },
   "outputs": [],
   "source": [
    "x = 2          # anything after a '#' is a comment\n",
    "y = 5\n",
    "xy = 'Hey'\n",
    "print(x+y, xy) # not really necessary as the last value in a bit of code is displayed by default"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    }
   },
   "source": [
    "Multiple variables can be assigned with the same value."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "outputs": [],
   "source": [
    "x = y = 1 \n",
    "print(x, y)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "The basic types build into Python include `int` (integers), `float` (floating point numbers), `complex` (complex number), `bool` (boolean) and `str` (unicode character strings). Some examples of each:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "outputs": [],
   "source": [
    "-1234567890   # an integer\n",
    "2.0           # a simple floating point number\n",
    "complex(1,2)\n",
    "(1+2j) # the same number as above\n",
    "True or False # the two possible boolean values\n",
    "'This is a string'\n",
    "\"It's another string\"\n",
    "print(\"\"\"Triple quotes (also with '''), allow strings to break over multiple lines.\n",
    "Alternatively \\n is a newline character (\\t for tab, \\\\ is a single backslash)\"\"\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "Python natively allows (nearly) infinite length integers while floating point numbers are double precision numbers:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "outputs": [],
   "source": [
    "11**300"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "scrolled": true,
    "slideshow": {
     "slide_type": "-"
    }
   },
   "source": [
    "```python\n",
    "11.0**300\n",
    "---------------------------------------------------------------------------\n",
    "OverflowError                             Traceback (most recent call last)\n",
    "<ipython-input-7-b61ab01789ad> in <module>()\n",
    "----> 1 11.0**300\n",
    "\n",
    "OverflowError: (34, 'Result too large')```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "### Arithmetic Operators"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "source": [
    "`+`: Addition  \n",
    "`-`: Subtraction  \n",
    "`/`: division  \n",
    "`%`: mod  \n",
    "`*`: multiplication  \n",
    "`//`: floor division  \n",
    "`**`: to the power of  "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "outputs": [],
   "source": [
    "1+2"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "outputs": [],
   "source": [
    "2-1"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "outputs": [],
   "source": [
    "1*2"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "outputs": [],
   "source": [
    "3/4"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    }
   },
   "source": [
    "In many languages (and older versions of python) 1/2 = 0 (truncated division). In Python 3 this behaviour is captured by a separate operator that rounds down: (ie a // b$=\\lfloor \\frac{a}{b}\\rfloor$)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "outputs": [],
   "source": [
    "3//4.0"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "outputs": [],
   "source": [
    "15%10"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "### Relational Operators"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "source": [
    "`==`: True, if it is equal  \n",
    "`!=`: True, if not equal to  \n",
    "`<`: less than  \n",
    "`>`: greater than  \n",
    "`<=`: less than or equal to  \n",
    "`>=`: greater than or equal to  \n",
    "\n",
    "Note the difference between `==` (equality test) and `=` (assignment)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "outputs": [],
   "source": [
    "z = 2\n",
    "z == 2"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "outputs": [],
   "source": [
    "z > 2"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "source": [
    "Comparisons can also be chained in the mathematically obvious way. The following will work as expected in Python (but not in other languages like C/C++):"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "outputs": [],
   "source": [
    "0.5 < z <= 1"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "#### Boolean and Bitwise Operators"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "source": [
    "\n",
    "`and`: Logical and | `&`: Bitwise and  \n",
    "`or`: Logical or | `|`: Bitwise or  \n",
    "`^`: Bitwise exclusive or  \n",
    "`not`: Logical not | `~`: Bitwise negation  \n",
    "`>>`: Bitwise riight shift  \n",
    "`<<`: Bitwise left shift"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "outputs": [],
   "source": [
    "a = 2 #binary: 10\n",
    "b = 3 #binary: 11\n",
    "print('a & b =',a & b,\"=\",bin(a&b))\n",
    "print('a | b =',a | b,\"=\",bin(a|b))\n",
    "print('a ^ b =',a ^ b,\"=\",bin(a^b))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "outputs": [],
   "source": [
    "print( not (True and False), \"==\", not True or not False)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "### Built-in Functions"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "source": [
    "Python comes with a wide range of functions. However many of these are part of stanard libraries like the `math` library rather than built-in."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "source": [
    "#### Converting values\n",
    "\n",
    "Conversion from hexadecimal to decimal is done by adding prefix **0x** to the hexadecimal value or vice versa by using built in [**hex( )**](https://docs.python.org/3/library/functions.html#hex), Octal to decimal by adding prefix **0** to the octal value or vice versa by using built in function [**oct( )**](https://docs.python.org/3/library/functions.html#oct)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "outputs": [],
   "source": [
    "hex(170)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "outputs": [],
   "source": [
    "0xAA"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "[**int( )** ](https://docs.python.org/3/library/functions.html#int)converts a number to an integer. This can be a single floating point number, integer or a string. For strings the base can optionally be specified:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "outputs": [],
   "source": [
    "print(int(7.7), int('111',2),int('7'))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    }
   },
   "source": [
    " Similarly, the function [**str( )**](https://docs.python.org/3/library/functions.html#func-str) can be used to convert almost anything to a string"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "outputs": [],
   "source": [
    "print(str(True),str(1.2345678),str(-2))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "#### Mathematical functions\n",
    "Mathematical functions include the usual suspects like logarithms, trigonometric fuctions, the constant $\\pi$ and so on."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "outputs": [],
   "source": [
    "import math\n",
    "math.sin(math.pi/2)\n",
    "from math import * # avoid having to put a math. in front of every mathematical function\n",
    "sin(pi/2) # equivalent to the statement above"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "#### Simplifying Arithmetic Operations"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "source": [
    "[**round( )**](https://docs.python.org/3/library/functions.html#round) function rounds the input value to a specified number of places or to the nearest integer. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "outputs": [],
   "source": [
    "print( round(5.6231) )\n",
    "print( round(4.55892, 2) )"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    }
   },
   "source": [
    "[**complex( )**](https://docs.python.org/3/library/functions.html#complex) is used to define a complex number and [**abs( )**](https://docs.python.org/3/library/functions.html#abs) outputs the absolute value of the same. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "outputs": [],
   "source": [
    "c =complex('5+2j')\n",
    "print( abs(c) )"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "[**divmod(x,y)**](https://docs.python.org/3/library/functions.html#divmod) outputs the quotient and the remainder in a tuple(you will be learning about it in the further chapters) in the format (quotient, remainder). "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "divmod(9,2)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    }
   },
   "source": [
    "#### Accepting User Inputs"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "source": [
    "[**input(prompt)**](https://docs.python.org/3/library/functions.html#input),  prompts for and returns input as a string. A useful function to use in conjunction with this is [**eval()**](https://docs.python.org/3/library/functions.html#eval) which takes a string and evaluates it as a python expression."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "source": [
    "```python\n",
    "abc =  input(\"abc = \")\n",
    "abcValue=eval(abc)\n",
    "print(abc,'=',abcValue)```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "## Working with strings\n",
    "\n",
    "### The Print Statement"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "source": [
    "As seen previously, The [**print()**](https://docs.python.org/3/library/functions.html#print) function prints all of its arguments as strings, separated by spaces and follows by a linebreak:\n",
    "\n",
    "    - print(\"Hello World\")\n",
    "    - print(\"Hello\",'World')\n",
    "    - print(\"Hello\", <Variable Containing the String>)\n",
    "\n",
    "Note that [**print**](https://docs.python.org/3/library/functions.html#print) is different in old versions of Python (2.7) where it was a statement and did not need parenthesis around its arguments."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "outputs": [],
   "source": [
    "print(\"Hello\",\"World\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "The print has some optional arguments to control where and how to print. This includes `sep` the separator (default space) and `end` (end charcter) and `file` to write to a file."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "outputs": [],
   "source": [
    "print(\"Hello\",\"World\",sep='...',end='!!')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "### String Formating\n",
    "\n",
    "There are lots of methods for formating and manipulating strings built into python. Some of these are illustrated here.\n",
    "\n",
    "String concatenation is the \"addition\" of two strings. Observe that while concatenating there will be no space between the strings."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "outputs": [],
   "source": [
    "string1='World'\n",
    "string2='!'\n",
    "print('Hello' + string1 + string2)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The [formatted string literal](https://docs.python.org/3/reference/lexical_analysis.html#f-strings) or [f-string](https://docs.python.org/3/reference/lexical_analysis.html#f-strings) is a string literal that is prefixed with 'f' or 'F'. These strings may contain replacement fields, which are expressions delimited by curly braces {}. While other string literals always have a constant value, formatted strings are really expressions evaluated at run time."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    }
   },
   "outputs": [],
   "source": [
    "d = 18\n",
    "f = 18.0\n",
    "o = oct(18)\n",
    "x = hex(18)\n",
    "\n",
    "print(f\"Actual Number = {d}\")\n",
    "print(f\"Float of the number = {f}\")\n",
    "print(f\"Octal equivalent of the number = {o}\")\n",
    "print(f\"Hexadecimal equivalent of the number = {x}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "### Other String Methods"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "source": [
    "Multiplying a string by an integer simply repeats it"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "outputs": [],
   "source": [
    "print(\"Hello World! \"*5)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    }
   },
   "source": [
    "Strings can be tranformed by a variety of functions:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "outputs": [],
   "source": [
    "s=\"hello wOrld\"\n",
    "s2 = \"     lots of space             \"\n",
    "print(s.capitalize())\n",
    "print(s.upper())\n",
    "print(s.lower())\n",
    "print(f'{s}'.center(30)) # center in 30 characters\n",
    "print(f'{s2}'.strip()) # remove leading and trailing whitespace\n",
    "print(\"Hello World\".replace(\"World\",\"Class\"))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "There are also lost of ways to inspect or check strings. Examples of a few of these are given here:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "outputs": [],
   "source": [
    "s=\"Hello World\"\n",
    "print(\"The length of '%s' is\"%s,len(s),\"characters\") # len() gives length\n",
    "s.startswith(\"Hello\") and s.endswith(\"World\") # check start/end\n",
    "# count strings\n",
    "print(\"There are %d 'l's but only %d World in %s\" % (s.count('l'),s.count('World'),s))\n",
    "print('\"el\" is at index',s.find('el'),\"in\",s) #index from 0 or -1"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "### String comparison operations\n",
    "Strings can be compared in lexicographical order with the usual comparisons. In addition the `in` operator checks for substrings:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "outputs": [],
   "source": [
    "'abc' < 'bbc' <= 'bbc'"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "outputs": [],
   "source": [
    "\"ABC\" in \"This is the ABC of Python\""
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "### Accessing parts of strings"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "source": [
    "Strings can be indexed with square brackets. Indexing starts from zero in Python. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "outputs": [],
   "source": [
    "s = '123456789'\n",
    "print('First charcter of',s,'is',s[0])\n",
    "print('Last charcter of',s,'is',s[len(s)-1])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    }
   },
   "source": [
    "Negative indices can be used to start counting from the back"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "outputs": [],
   "source": [
    "print('First charcter of',s,'is',s[-len(s)])\n",
    "print('Last charcter of',s,'is',s[-1])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "Finally a substring (range of characters) an be specified as using $a:b$ to specify the characters at index $a,a+1,\\ldots,b-1$. Note that the last charcter is *not* included."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "outputs": [],
   "source": [
    "print(\"First three charcters\",s[0:3])\n",
    "print(\"Next three characters\",s[3:6])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    }
   },
   "source": [
    "An empty beginning and end of the range denotes the beginning/end of the string:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "outputs": [],
   "source": [
    "print(\"First three characters\", s[:3])\n",
    "print(\"Last three characters\", s[-3:])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "### Strings are immutable\n",
    "\n",
    "It is important that strings are constant, immutable values in Python. While new strings can easily be created it is not possible to modify a string:"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "scrolled": true,
    "slideshow": {
     "slide_type": "-"
    }
   },
   "source": [
    "```python\n",
    "s='012345'\n",
    "sX=s[:2]+'X'+s[3:] # this creates a new string with 2 replaced by X\n",
    "print(\"creating new string\",sX,\"OK\")\n",
    "sX=s.replace('2','X') # the same thing\n",
    "print(sX,\"still OK\")\n",
    "s[2] = 'X' # an error!!!\n",
    "\n",
    "creating new string 01X345 OK\n",
    "01X345 still OK\n",
    "---------------------------------------------------------------------------\n",
    "TypeError                                 Traceback (most recent call last)\n",
    "<ipython-input-38-93bf77b20e7d> in <module>()\n",
    "      4 sX=s.replace('2','X') # the same thing\n",
    "      5 print(sX,\"still OK\")\n",
    "----> 6 s[2] = 'X' # an error!!!\n",
    "\n",
    "TypeError: 'str' object does not support item assignment```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "## Data Structures"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "source": [
    "In simple terms, it is the the collection or group of data in a particular structure."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    }
   },
   "source": [
    "### [Lists](https://docs.python.org/3/tutorial/datastructures.html#more-on-lists)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "source": [
    "Lists are the most commonly used data structure. Think of it as a sequence of data that is enclosed in square brackets and data are separated by a comma. Each of these data can be accessed by calling it's index value.\n",
    "\n",
    "Lists are declared by just equating a variable to '[ ]' or list."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "outputs": [],
   "source": [
    "a = []"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "outputs": [],
   "source": [
    "type(a)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "One can directly assign the sequence of data to a list x as shown."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "outputs": [],
   "source": [
    "x = ['apple', 'orange']"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    }
   },
   "source": [
    "#### Indexing"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "source": [
    "In python, indexing starts from 0 as already seen for strings. Thus now the list x, which has two elements will have apple at 0 index and orange at 1 index. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "outputs": [],
   "source": [
    "x[0]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    }
   },
   "source": [
    "Indexing can also be done in reverse order. That is the last element can be accessed first. Here, indexing starts from -1. Thus index value -1 will be orange and index -2 will be apple."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "outputs": [],
   "source": [
    "x[-1]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "As you might have already guessed, x[0] = x[-2], x[1] = x[-1]. This concept can be extended towards lists with more many elements."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    }
   },
   "outputs": [],
   "source": [
    "y = ['carrot','potato']"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    }
   },
   "source": [
    "Here we have declared two lists x and y each containing its own data. Now, these two lists can again be put into another list say z which will have it's data as two lists. This list inside a list is called as nested lists and is how an array would be declared which we will see later."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    }
   },
   "outputs": [],
   "source": [
    "z  = [x,y]\n",
    "print( z )"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "Indexing in nested lists can be quite confusing if you do not understand how indexing works in python. So let us break it down and then arrive at a conclusion.\n",
    "\n",
    "Let us access the data 'apple' in the above nested list.\n",
    "First, at index 0 there is a list ['apple','orange'] and at index 1 there is another list ['carrot','potato']. Hence z[0] should give us the first list which contains 'apple' and 'orange'. From this list we can take the second element (index 1) to get 'orange'"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "outputs": [],
   "source": [
    "print( z )"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "outputs": [],
   "source": [
    "print(z[0][1])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "Lists do not have to be homogenous. Each element can be of a different type:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "outputs": [],
   "source": [
    "[\"this is a valid list\",2,3.6,(1+2j),[\"a\",\"sublist\"]]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "#### Slicing"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "source": [
    "Indexing was only limited to accessing a single element, Slicing on the other hand is accessing a sequence of data inside the list. In other words \"slicing\" the list.\n",
    "\n",
    "Slicing is done by defining the index values of the first element and the last element from the parent list that is required in the sliced list. It is written as parentlist[ a : b ] where a,b are the index values from the parent list. If a or b is not defined then the index value is considered to be the first value for a if a is not defined and the last value for b when b is not defined."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "outputs": [],
   "source": [
    "num = [0,1,2,3,4,5,6,7,8,9]\n",
    "print(num[0:4])\n",
    "print(num[4:])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "You can also slice a parent list with a fixed length or step length."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "outputs": [],
   "source": [
    "num[:9:3]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "#### Built in List Functions"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "source": [
    "To find the length of the list or the number of elements in a list, [**len( )**](https://docs.python.org/3/library/functions.html#len) is used."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "outputs": [],
   "source": [
    "len(num)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    }
   },
   "source": [
    "If the list consists of all integer elements then [**min( )**](https://docs.python.org/3/library/functions.html#min) and [**max( )**](https://docs.python.org/3/library/functions.html#max) gives the minimum and maximum value in the list. Similarly [**sum**](https://docs.python.org/3/library/functions.html#sum) is the sum"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "outputs": [],
   "source": [
    "print(\"min =\",min(num),\"  max =\",max(num),\"  total =\",sum(num))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "outputs": [],
   "source": [
    "max(num)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "Lists can be concatenated by adding, '+' them. The resultant list will contain all the elements of the lists that were added. The resultant list will not be a nested list."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "outputs": [],
   "source": [
    "[1,2,3] + [5,4,7]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    }
   },
   "source": [
    "There might arise a requirement where you might need to check if a particular element is there in a predefined list. Consider the below list."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "outputs": [],
   "source": [
    "names = ['Earth','Air','Fire','Water']"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    }
   },
   "source": [
    "To check if 'Fire' and 'Rajath' is present in the list names. A conventional approach would be to use a for loop and iterate over the list and use the if condition. But in python you can use 'a in b' concept which would return 'True' if a is present in b and 'False' if not."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "outputs": [],
   "source": [
    "'Fire' in names"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "outputs": [],
   "source": [
    "'Space' in names"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "In a list with string elements, [**max( )**](https://docs.python.org/3/library/functions.html#max) and [**min( )**](https://docs.python.org/3/library/functions.html#min) are still applicable and return the first/last element in lexicographical order. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "outputs": [],
   "source": [
    "mlist = ['bzaa','ds','nc','az','z','klm']\n",
    "print(\"max =\",max(mlist))\n",
    "print(\"min =\",min(mlist))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    }
   },
   "source": [
    "Here the first index of each element is considered and thus z has the highest ASCII value thus it is returned and minimum ASCII is a. But what if numbers are declared as strings?"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "outputs": [],
   "source": [
    "nlist = ['1','94','93','1000']\n",
    "print(\"max =\",max(nlist))\n",
    "print('min =',min(nlist))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "Even if the numbers are declared in a string the first index of each element is considered and the maximum and minimum values are returned accordingly."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "source": [
    "But if you want to find the [**max( )**](https://docs.python.org/3/library/functions.html#max)  string element based on the length of the string then another parameter `key` can be used to specify the function to use for generating the value on which to sort. Hence finding the longest and shortest string in `mlist` can be doen using the [`len`](https://docs.python.org/3/library/functions.html#len) function:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "outputs": [],
   "source": [
    "print('longest =',max(mlist, key=len))\n",
    "print('shortest =',min(mlist, key=len))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "source": [
    "Any other built-in or user defined function can be used."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "A string can be converted into a list by using the [**list()**](https://docs.python.org/3/library/stdtypes.html#typesseq-list) function, or more usefully using the [**split()**](https://docs.python.org/3.6/library/stdtypes.html#str.split) method, which breaks strings up based on spaces."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "outputs": [],
   "source": [
    "print(list('hello world !'),'Hello   World !!'.split())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    }
   },
   "source": [
    "[**append( )**](https://docs.python.org/3.6/tutorial/datastructures.html) is used to add a single element at the end of the list."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "outputs": [],
   "source": [
    "lst = [1,1,4,8,7]\n",
    "lst.append(1)\n",
    "print(lst)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    }
   },
   "source": [
    "Appending a list to a list would create a sublist. If a nested list is not what is desired then the [**extend( )**](https://docs.python.org/3.6/tutorial/datastructures.html) function can be used."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "outputs": [],
   "source": [
    "lst.extend([10,11,12])\n",
    "print(lst)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    }
   },
   "source": [
    "[**count( )**](https://docs.python.org/3.6/tutorial/datastructures.html) is used to count the number of a particular element that is present in the list. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "outputs": [],
   "source": [
    "lst.count(1)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "[**index( )**](https://docs.python.org/3.6/tutorial/datastructures.html) is used to find the index value of a particular element. Note that if there are multiple elements of the same value then the first index value of that element is returned."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "outputs": [],
   "source": [
    "lst.index(1)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    }
   },
   "source": [
    "[**insert(x,y)**](https://docs.python.org/3.6/tutorial/datastructures.html) is used to insert a element y at a specified index value x. [**append( )**](https://docs.python.org/3.6/tutorial/datastructures.html) function made it only possible to insert at the end. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "outputs": [],
   "source": [
    "lst.insert(5, 'name')\n",
    "print(lst)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    }
   },
   "source": [
    "[**insert(x,y)**](https://docs.python.org/3.6/tutorial/datastructures.html) inserts but does not replace element. If you want to replace the element with another element you simply assign the value to that particular index."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "outputs": [],
   "source": [
    "lst[5] = 'Python'\n",
    "print(lst)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "[**pop( )**](https://docs.python.org/3.6/tutorial/datastructures.html) function return the last element in the list. This is similar to the operation of a stack. Hence it wouldn't be wrong to tell that lists can be used as a stack."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "outputs": [],
   "source": [
    "lst.pop()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    }
   },
   "source": [
    "Index value can be specified to pop a ceratin element corresponding to that index value."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "outputs": [],
   "source": [
    "lst.pop(0)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    }
   },
   "source": [
    "[**pop( )**](https://docs.python.org/3.6/tutorial/datastructures.html) is used to remove element based on it's index value which can be assigned to a variable. One can also remove element by specifying the element itself using the [**remove( )**](https://docs.python.org/3.6/tutorial/datastructures.html) function."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "outputs": [],
   "source": [
    "lst.remove('Python')\n",
    "print(lst)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    }
   },
   "source": [
    "Alternative to [**remove**](https://docs.python.org/3.6/tutorial/datastructures.html) function but with using index value is [**del**](https://docs.python.org/3/tutorial/datastructures.html#the-del-statement)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "outputs": [],
   "source": [
    "del lst[1]\n",
    "print(lst)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "The entire elements present in the list can be reversed by using the [**reverse()**](https://docs.python.org/3/tutorial/datastructures.html#using-lists-as-stacks) function."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "outputs": [],
   "source": [
    "lst.reverse()\n",
    "print(lst)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "Python offers built in operation [**sort( )**](https://docs.python.org/3/tutorial/datastructures.html#using-lists-as-stacks) to arrange the elements in ascending order. Alternatively [**sorted()**](https://docs.python.org/3/library/functions.html#sorted) can be used to construct a copy of the list in sorted order"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "outputs": [],
   "source": [
    "lst.sort()\n",
    "print(lst)\n",
    "print(sorted([3,2,1])) # another way to sort"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    }
   },
   "source": [
    "For descending order, By default the reverse condition will be False for reverse. Hence changing it to True would arrange the elements in descending order."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "outputs": [],
   "source": [
    "lst.sort(reverse=True)\n",
    "print(lst)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "Similarly for lists containing string elements, [**sort( )**](https://docs.python.org/3/tutorial/datastructures.html#using-lists-as-stacks) would sort the elements based on it's ASCII value in ascending and by specifying reverse=True in descending."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "outputs": [],
   "source": [
    "names.sort()\n",
    "print(names)\n",
    "names.sort(reverse=True)\n",
    "print(names)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    }
   },
   "source": [
    "To sort based on length key=len should be specified as shown."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "outputs": [],
   "source": [
    "names.sort(key=len)\n",
    "print(names)\n",
    "print(sorted(names,key=len,reverse=True))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "#### Copying a list"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "source": [
    "Assignment of a list does not imply copying. It simply creates a second reference to the same list. Most of new python programmers get caught out by this initially. Consider the following,"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "outputs": [],
   "source": [
    "lista= [2,1,4,3]\n",
    "listb = lista\n",
    "print(listb)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    }
   },
   "source": [
    "Here, We have declared a list, lista = [2,1,4,3]. This list is copied to listb by assigning it's value and it get's copied as seen. Now we perform some random operations on lista."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "outputs": [],
   "source": [
    "lista.sort()\n",
    "lista.pop()\n",
    "lista.append(9)\n",
    "print(\"A =\",lista)\n",
    "print(\"B =\",listb)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "listb has also changed though no operation has been performed on it. This is because you have assigned the same memory space of lista to listb. So how do fix this?\n",
    "\n",
    "If you recall, in slicing we had seen that parentlist[a:b] returns a list from parent list with start index a and end index b and if a and b is not mentioned then by default it considers the first and last element. We use the same concept here. By doing so, we are assigning the data of lista to listb as a variable."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "outputs": [],
   "source": [
    "lista = [2,1,4,3]\n",
    "listb = lista[:] # make a copy by taking a slice from beginning to end\n",
    "print(\"Starting with:\")\n",
    "print(\"A =\",lista)\n",
    "print(\"B =\",listb)\n",
    "lista.sort()\n",
    "lista.pop()\n",
    "lista.append(9)\n",
    "print(\"Finnished with:\")\n",
    "print(\"A =\",lista)\n",
    "print(\"B =\",listb)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "#### [List comprehension](https://docs.python.org/3/tutorial/datastructures.html#list-comprehensions)\n",
    "A very powerful concept in Python (that also applies to Tuples, sets and dictionaries as we will see below), is the ability to define lists using list comprehension (looping) expression. For example:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "outputs": [],
   "source": [
    "[i**2 for i in [1,2,3]]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    }
   },
   "source": [
    "As can be seen this constructs a new list by taking each element of the original `[1,2,3]` and squaring it. We can have multiple such implied loops to get for example:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "outputs": [],
   "source": [
    "[10*i+j for i in [1,2,3] for j in [5,7]]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    }
   },
   "source": [
    "Finally the looping can be filtered using an [**if**](https://docs.python.org/3/tutorial/controlflow.html#if-statements) expression with the [**for** - **in**](https://docs.python.org/3/tutorial/controlflow.html#for-statements) construct."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "outputs": [],
   "source": [
    "[10*i+j for i in [1,2,3] if i%2==1 for j in [4,5,7] if j >= i+4] # keep odd i and  j larger than i+3 only"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "### [Tuples](https://docs.python.org/3/tutorial/datastructures.html#tuples-and-sequences)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "source": [
    "Tuples are similar to lists but only big difference is the elements inside a list can be changed but in tuple it cannot be changed. Think of tuples as something which has to be True for a particular something and cannot be True for no other values. For better understanding, Recall [**divmod()**](https://docs.python.org/3/library/functions.html#divmod) function."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "outputs": [],
   "source": [
    "xyz = divmod(10,3)\n",
    "print(xyz)\n",
    "print(type(xyz))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "source": [
    "Here the quotient has to be 3 and the remainder has to be 1. These values cannot be changed whatsoever when 10 is divided by 3. Hence divmod returns these values in a tuple."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "To define a tuple, A variable is assigned to paranthesis ( ) or tuple( )."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "outputs": [],
   "source": [
    "tup = ()\n",
    "tup2 = tuple()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    }
   },
   "source": [
    "If you want to directly declare a tuple it can be done by using a comma at the end of the data."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "outputs": [],
   "source": [
    "27,"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    }
   },
   "source": [
    "27 when multiplied by 2 yields 54, But when multiplied with a tuple the data is repeated twice."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "outputs": [],
   "source": [
    "2*(27,)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "Values can be assigned while declaring a tuple. It takes a list as input and converts it into a tuple or it takes a string and converts it into a tuple."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "outputs": [],
   "source": [
    "tup3 = tuple([1,2,3])\n",
    "print(tup3)\n",
    "tup4 = tuple('Hello')\n",
    "print(tup4)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    }
   },
   "source": [
    "It follows the same indexing and slicing as Lists."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "outputs": [],
   "source": [
    "print(tup3[1])\n",
    "tup5 = tup4[:3]\n",
    "print(tup5)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "#### Mapping one tuple to another\n",
    "Tupples can be used as the left hand side of assignments and are matched to the correct right hand side elements - assuming they have the right length"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "outputs": [],
   "source": [
    "(a,b,c)= ('alpha','beta','gamma') # are optional\n",
    "a,b,c= 'alpha','beta','gamma' # The same as the above\n",
    "print(a,b,c)\n",
    "a,b,c = ['Alpha','Beta','Gamma'] # can assign lists\n",
    "print(a,b,c)\n",
    "[a,b,c]=('this','is','ok') # even this is OK\n",
    "print(a,b,c)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "More complex nexted unpackings of values are also possible"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "outputs": [],
   "source": [
    "(w,(x,y),z)=(1,(2,3),4)\n",
    "print(w,x,y,z)\n",
    "(w,xy,z)=(1,(2,3),4)\n",
    "print(w,xy,z) # notice that xy is now a tuple"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "#### Built In Tuple functions"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "source": [
    "[**count()**](https://docs.python.org/3/tutorial/datastructures.html#more-on-lists) function counts the number of specified element that is present in the tuple."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "outputs": [],
   "source": [
    "d=tuple('a string with many \"a\"s')\n",
    "d.count('a')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    }
   },
   "source": [
    "[**index()**](https://docs.python.org/3/tutorial/datastructures.html#more-on-lists) function returns the index of the specified element. If the elements are more than one then the index of the first element of that specified element is returned"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "outputs": [],
   "source": [
    "d.index('a')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "### [Sets](https://docs.python.org/3/tutorial/datastructures.html#sets)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "source": [
    "Sets are mainly used to eliminate repeated numbers in a sequence/list. It is also used to perform some standard set operations.\n",
    "\n",
    "Sets are declared as set() which will initialize a empty set. Also [`set([sequence])`](https://docs.python.org/3/tutorial/datastructures.html#sets) can be executed to declare a set with elements"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "outputs": [],
   "source": [
    "set1 = set()\n",
    "print(type(set1))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "outputs": [],
   "source": [
    "set0 = set([1,2,2,3,3,4])\n",
    "set0 = {1,2,2,3,3,4} # equivalent to the above\n",
    "print(set0)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "source": [
    "elements 2,3 which are repeated twice are seen only once. Thus in a set each element is distinct."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "skip"
    }
   },
   "source": [
    "However be warned that **{}** is **NOT** a set, but a dictionary (see next chapter of this tutorial)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "skip"
    }
   },
   "outputs": [],
   "source": [
    "type({})"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "#### Built-in Functions"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "outputs": [],
   "source": [
    "set1 = set([1,2,3])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "outputs": [],
   "source": [
    "set2 = set([2,3,4,5])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    }
   },
   "source": [
    "[**union( )**](https://docs.python.org/3.6/library/stdtypes.html?highlight=set%20union#set-types-set-frozenset) function returns a set which contains all the elements of both the sets without repition."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    }
   },
   "outputs": [],
   "source": [
    "set1.union(set2)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "[**add( )**](https://docs.python.org/3.6/library/stdtypes.html?highlight=set%20union#set-types-set-frozenset) will add a particular element into the set. Note that the index of the newly added element is arbitrary and can be placed anywhere not neccessarily in the end."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "outputs": [],
   "source": [
    "set1.add(0)\n",
    "set1"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    }
   },
   "source": [
    "[**intersection( )**](https://docs.python.org/3.6/library/stdtypes.html?highlight=set%20union#set-types-set-frozenset) function outputs a set which contains all the elements that are in both sets."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "outputs": [],
   "source": [
    "set1.intersection(set2)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    }
   },
   "source": [
    "[**difference( )**](https://docs.python.org/3.6/library/stdtypes.html?highlight=set%20union#set-types-set-frozenset) function ouptuts a set which contains elements that are in set1 and not in set2."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "outputs": [],
   "source": [
    "set1.difference(set2)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "skip"
    }
   },
   "source": [
    "[**symmetric_difference( )**](https://docs.python.org/3.6/library/stdtypes.html?highlight=set%20union#set-types-set-frozenset) function ouputs a function which contains elements that are in one of the sets."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "skip"
    }
   },
   "outputs": [],
   "source": [
    "set2.symmetric_difference(set1)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "skip"
    }
   },
   "source": [
    "**[issubset( )](https://docs.python.org/3.6/library/stdtypes.html?highlight=set%20union#set-types-set-frozenset), [isdisjoint( )](https://docs.python.org/3.6/library/stdtypes.html?highlight=set%20union#set-types-set-frozenset), [issuperset( )](https://docs.python.org/3.6/library/stdtypes.html?highlight=set%20union#set-types-set-frozenset)** is used to check if the set1/set2 is a subset, disjoint or superset of set2/set1 respectively."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "skip"
    }
   },
   "outputs": [],
   "source": [
    "set1.issubset(set2)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "skip"
    }
   },
   "outputs": [],
   "source": [
    "set2.isdisjoint(set1)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "skip"
    }
   },
   "outputs": [],
   "source": [
    "set2.issuperset(set1)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "skip"
    }
   },
   "source": [
    "[**pop( )**](https://docs.python.org/3.6/library/stdtypes.html?highlight=set%20union#set-types-set-frozenset) is used to remove an arbitrary element in the set"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "skip"
    }
   },
   "outputs": [],
   "source": [
    "set1.pop()\n",
    "print(set1)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "[**remove( )**](https://docs.python.org/3.6/library/stdtypes.html?highlight=set%20union#set-types-set-frozenset) function deletes the specified element from the set."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "outputs": [],
   "source": [
    "set1.remove(2)\n",
    "set1"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    }
   },
   "source": [
    "[**clear( )**](https://docs.python.org/3.6/library/stdtypes.html?highlight=set%20union#set-types-set-frozenset) is used to clear all the elements and make that set an empty set."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "outputs": [],
   "source": [
    "set1.clear()\n",
    "set1"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "### Strings"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "source": [
    "Strings have already been discussed in Chapter 02, but can also be treated as collections similar to lists and tuples.\n",
    "For example"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "outputs": [],
   "source": [
    "S = 'Taj Mahal is beautiful'\n",
    "print([x for x in S if x.islower()]) # list of lower case charactes\n",
    "words=S.split() # list of words\n",
    "print(\"Words are:\",words)\n",
    "print(\"--\".join(words)) # hyphenated \n",
    "\" \".join(w.capitalize() for w in words) # capitalise words"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "String Indexing and Slicing are similar to Lists which was explained in detail earlier."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "outputs": [],
   "source": [
    "print(S[4])\n",
    "print(S[4:])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "### [Dictionaries](https://docs.python.org/3/tutorial/datastructures.html#dictionaries)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "source": [
    "Dictionaries are mappings between keys and items stored in the dictionaries. Alternatively one can think of dictionaries as sets in which something stored against every element of the set. They can be defined as follows:"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "source": [
    "To define a dictionary, equate a variable to { } or dict()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "outputs": [],
   "source": [
    "d = dict() # or equivalently d={}\n",
    "print(type(d))\n",
    "d['abc'] = 3\n",
    "d[4] = \"A string\"\n",
    "print(d)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    }
   },
   "source": [
    "As can be guessed from the output above. Dictionaries can be defined by using the `{ key : value }` syntax. The following dictionary has three elements"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "outputs": [],
   "source": [
    "d = { 1: 'One', 2 : 'Two', 100 : 'Hundred'}\n",
    "len(d)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    }
   },
   "source": [
    "Now you are able to access 'One' by the index value set at 1"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "outputs": [],
   "source": [
    "print(d[1])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    }
   },
   "source": [
    "There are a number of alternative ways for specifying a dictionary including as a list of `(key,value)` tuples.\n",
    "To illustrate this we will start with two lists and form a set of tuples from them using the [**zip()**](https://docs.python.org/3/library/functions.html#zip) function\n",
    "Two lists which are related can be merged to form a dictionary."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "outputs": [],
   "source": [
    "names = ['One', 'Two', 'Three', 'Four', 'Five']\n",
    "numbers = [1, 2, 3, 4, 5]\n",
    "[ (name,number) for name,number in zip(names,numbers)] # create (name,number) pairs"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "Now we can create a dictionary that maps the name to the number as follows."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "outputs": [],
   "source": [
    "a1 = dict((name,number) for name,number in zip(names,numbers))\n",
    "print(a1)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "Note that the ordering for this dictionary is not based on the order in which elements are added but on its own ordering (based on hash index ordering). It is best never to assume an ordering when iterating over elements of a dictionary.\n",
    "\n",
    "By using tuples as indexes we make a dictionary behave like a sparse matrix:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "outputs": [],
   "source": [
    "matrix={ (0,1): 3.5, (2,17): 0.1}\n",
    "matrix[2,2] = matrix[0,1] + matrix[2,17]\n",
    "print(matrix)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    }
   },
   "source": [
    "Dictionary can also be built using the loop style definition."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "outputs": [],
   "source": [
    "a2 = { name : len(name) for name in names}\n",
    "print(a2)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "#### Built-in Functions"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "source": [
    "The [**len()**](https://docs.python.org/3/library/functions.html#len) function and **in** operator have the obvious meaning:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "outputs": [],
   "source": [
    "print(\"a1 has\",len(a1),\"elements\")\n",
    "print(\"One is in a1\",'One' in a1,\"but not Zero\", 'Zero' in a1)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    }
   },
   "source": [
    "[**clear( )**](https://docs.python.org/3.6/library/stdtypes.html?highlight=set%20union#mapping-types-dict) function is used to erase all elements."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "outputs": [],
   "source": [
    "a2.clear()\n",
    "print(a2)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "[**values( )**](https://docs.python.org/3.6/library/stdtypes.html?highlight=set%20union#mapping-types-dict) function returns a list with all the assigned values in the dictionary. (Acutally not quit a list, but something that we can iterate over just like a list to construct a list, tuple or any other collection):"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "outputs": [],
   "source": [
    "[ v for v in a1.values() ]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    }
   },
   "source": [
    "[**keys( )**](https://docs.python.org/3.6/library/stdtypes.html?highlight=set%20union#mapping-types-dict) function returns all the index or the keys to which contains the values that it was assigned to."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "outputs": [],
   "source": [
    "{ k for k in a1.keys() }"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "[**items( )**](https://docs.python.org/3.6/library/stdtypes.html?highlight=set%20union#mapping-types-dict) is returns a list containing both the list but each element in the dictionary is inside a tuple. This is same as the result that was obtained when zip function was used - except that the ordering has been 'shuffled' by the dictionary."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "outputs": [],
   "source": [
    "\",  \".join( \"%s = %d\" % (name,val) for name,val in a1.items())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    }
   },
   "source": [
    "[**pop( )**](https://docs.python.org/3.6/library/stdtypes.html?highlight=set%20union#mapping-types-dict) function is used to get the remove that particular element and this removed element can be assigned to a new variable. But remember only the value is stored and not the key. Because the is just a index value."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "outputs": [],
   "source": [
    "val = a1.pop('Four')\n",
    "print(a1)\n",
    "print(\"Removed\",val)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "## [Control Flow Statements](https://docs.python.org/3/tutorial/controlflow.html)\n",
    "The key thing to note about Python's control flow statements and program structure is that it uses _indentation_ to mark blocks. Hence the amount of white space (space or tab characters) at the start of a line is very important. This generally helps to make code more readable but can catch out new users of python."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "source": [
    "### Conditionals\n",
    "\n",
    "#### [If](https://docs.python.org/3/reference/compound_stmts.html#if)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "source": [
    "```python\n",
    "if some_condition:\n",
    "    code block```"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    }
   },
   "outputs": [],
   "source": [
    "x = 12\n",
    "if x > 10:\n",
    "    print(\"Hello\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "#### [If-else](https://docs.python.org/3/reference/compound_stmts.html#else)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "source": [
    "```python\n",
    "if some_condition:\n",
    "    algorithm\n",
    "else:\n",
    "    algorithm```\n",
    "    "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "outputs": [],
   "source": [
    "x = 12\n",
    "if 10 < x < 11:\n",
    "    print(\"hello\")\n",
    "else:\n",
    "    print(\"world\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "#### [Else if](https://docs.python.org/3/reference/compound_stmts.html#elif)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "source": [
    "```python\n",
    "if some_condition:  \n",
    "    algorithm\n",
    "elif some_condition:\n",
    "    algorithm\n",
    "else:\n",
    "    algorithm```"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "outputs": [],
   "source": [
    "x = 10\n",
    "y = 12\n",
    "if x > y:\n",
    "    print(\"x>y\")\n",
    "elif x < y:\n",
    "    print(\"x<y\")\n",
    "else:\n",
    "    print(\"x=y\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "if statement inside a if statement or if-elif or if-else are called as nested if statements."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "outputs": [],
   "source": [
    "x = 10\n",
    "y = 12\n",
    "if x > y:\n",
    "    print( \"x>y\")\n",
    "elif x < y:\n",
    "    print( \"x<y\")\n",
    "    if x==10:\n",
    "        print (\"x=10\")\n",
    "    else:\n",
    "        print (\"invalid\")\n",
    "else:\n",
    "    print (\"x=y\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "### Loops"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "source": [
    "#### [For](https://docs.python.org/3/reference/compound_stmts.html#for)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "source": [
    "```python\n",
    "for variable in something:\n",
    "    algorithm```\n",
    "    \n",
    "When looping over integers the **range()** function is useful which generates a range of integers:\n",
    "* range(n) =  0, 1, ..., n-1\n",
    "* range(m,n)= m, m+1, ..., n-1\n",
    "* range(m,n,s)= m, m+s, m+2s, ..., m + ((n-m-1)//s) * s"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "outputs": [],
   "source": [
    "for ch in 'abc':\n",
    "    print(ch)\n",
    "total = 0\n",
    "for i in range(5):\n",
    "    total += i\n",
    "for i,j in [(1,2),(3,1)]:\n",
    "    total += i**j\n",
    "print(\"total =\",total)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    }
   },
   "source": [
    "In the above example, i iterates over the 0,1,2,3,4. Every time it takes each value and executes the algorithm inside the loop. It is also possible to iterate over a nested list illustrated below."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "outputs": [],
   "source": [
    "list_of_lists = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]\n",
    "for list1 in list_of_lists:\n",
    "        print(list1)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "A use case of a nested for loop in this case would be,"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "outputs": [],
   "source": [
    "list_of_lists = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]\n",
    "total=0\n",
    "for list1 in list_of_lists:\n",
    "    for x in list1:\n",
    "        total = total+x\n",
    "print(total)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "There are many helper functions that make [**for**](https://docs.python.org/3/reference/compound_stmts.html#for) loops even more powerful and easy to use. For example [**enumerate()**](https://docs.python.org/3/library/functions.html#enumerate), [**zip()**](https://docs.python.org/3/library/functions.html#zip), [**sorted()**](https://docs.python.org/3/library/functions.html#sorted), [**reversed()**](https://docs.python.org/3/library/functions.html#reversed)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "outputs": [],
   "source": [
    "print(\"reversed: \",end=\"\")\n",
    "for ch in reversed(\"abc\"):\n",
    "    print(ch,end=\";\")\n",
    "print(\"\\nenuemerated: \")\n",
    "for i,ch in enumerate(\"abc\"):\n",
    "    print(i,\"=\",ch,end=\"; \")\n",
    "print(\"\\nzip'ed: \")\n",
    "for a,x in zip(\"abc\",\"xyz\"):\n",
    "    print(a,\":\",x)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "#### [While](https://docs.python.org/3/reference/compound_stmts.html#while)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "source": [
    "```python\n",
    "while some_condition:  \n",
    "    algorithm```"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    }
   },
   "outputs": [],
   "source": [
    "i = 1\n",
    "while i < 3:\n",
    "    print(i ** 2)\n",
    "    i = i+1\n",
    "print('Bye')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "#### [Break](https://docs.python.org/3/reference/simple_stmts.html#break)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "source": [
    "As the name says. It is used to break out of a loop when a condition becomes true when executing the loop."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "outputs": [],
   "source": [
    "for i in range(100):\n",
    "    print(i)\n",
    "    if i>=7:\n",
    "        break"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "#### [Continue](https://docs.python.org/3/reference/simple_stmts.html#continue)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "source": [
    "This continues the rest of the loop. Sometimes when a condition is satisfied there are chances of the loop getting terminated. This can be avoided using continue statement. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "outputs": [],
   "source": [
    "for i in range(10):\n",
    "    if i>4:\n",
    "        print(\"Ignored\",i)\n",
    "        continue\n",
    "    # this statement is not reach if i > 4\n",
    "    print(\"Processed\",i)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "### [Catching exceptions](https://docs.python.org/3/reference/compound_stmts.html#try)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "source": [
    "To break out of deeply nested exectution sometimes it is useful to raise an exception.\n",
    "A try block allows you to catch exceptions that happen anywhere during the exeuction of the try block:\n",
    "```python\n",
    "try:\n",
    "    code\n",
    "except <Exception Type> as <variable name>:\n",
    "    # deal with error of this type\n",
    "except:\n",
    "    # deal with any error```"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "outputs": [],
   "source": [
    "try:\n",
    "    count=0\n",
    "    while True:\n",
    "        while True:\n",
    "            while True:\n",
    "                print(\"Looping\")\n",
    "                count = count + 1\n",
    "                if count > 3:\n",
    "                    raise Exception(\"abort\") # exit every loop or function\n",
    "except Exception as e: # this is where we go when an exception is raised\n",
    "    print(\"Caught exception:\",e)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "This can also be useful to handle unexpected system errors more gracefully:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "outputs": [],
   "source": [
    "try:\n",
    "    for i in [2,1.5,0.0,3]:\n",
    "        inverse = 1.0/i\n",
    "except: # no matter what exception\n",
    "    print(\"Cannot calculate inverse\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "## [Functions](https://docs.python.org/3/tutorial/controlflow.html#defining-functions)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "source": [
    "Functions can represent mathematical functions. More importantly, in programmming functions are a mechansim to allow code to be re-used so that complex programs can be built up out of simpler parts. "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "source": [
    "This is the basic syntax of a function\n",
    "\n",
    "```python\n",
    "def funcname(arg1, arg2,... argN):\n",
    "    ''' Document String'''\n",
    "    statements\n",
    "    return <value>```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    }
   },
   "source": [
    "Read the above syntax as, A function by name \"funcname\" is defined, which accepts arguements \"arg1,arg2,....argN\". The function is documented and it is '''Document String'''. The function after executing the statements returns a \"value\".\n",
    "\n",
    "Return values are optional (by default every function returns **None** if no return statement is executed)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "outputs": [],
   "source": [
    "print(\"Hello Jack.\")\n",
    "print(\"Jack, how are you?\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "source": [
    "Instead of writing the above two statements every single time it can be replaced by defining a function which would do the job in just one line. \n",
    "\n",
    "Defining a function firstfunc()."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    }
   },
   "outputs": [],
   "source": [
    "def firstfunc():\n",
    "    print(\"Hello Jack.\")\n",
    "    print(\"Jack, how are you?\")\n",
    "firstfunc() # execute the function"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "**firstfunc()** every time just prints the message to a single person. We can make our function **firstfunc()** to accept arguements which will store the name and then prints respective to that accepted name. To do so, add a argument within the function as shown."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    }
   },
   "outputs": [],
   "source": [
    "def firstfunc(username):\n",
    "    print(\"Hello %s.\" % username)\n",
    "    print(username + ',' ,\"how are you?\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    }
   },
   "outputs": [],
   "source": [
    "name1 = 'sally' # or use input('Please enter your name : ')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "source": [
    " So we pass this variable to the function **firstfunc()** as the variable username because that is the variable that is defined for this function. i.e name1 is passed as username."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    }
   },
   "outputs": [],
   "source": [
    "firstfunc(name1)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "### [Return Statement](https://docs.python.org/3/reference/simple_stmts.html#grammar-token-return_stmt)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "source": [
    "When the function results in some value and that value has to be stored in a variable or needs to be sent back or returned for further operation to the main algorithm, a return statement is used."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "outputs": [],
   "source": [
    "def times(x,y):\n",
    "    z = x*y\n",
    "    return z"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    }
   },
   "source": [
    "The above defined **times( )** function accepts two arguements and return the variable z which contains the result of the product of the two arguements"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "outputs": [],
   "source": [
    "c = times(4,5)\n",
    "print(c)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "source": [
    "The z value is stored in variable c and can be used for further operations."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "Instead of declaring another variable the entire statement itself can be used in the return statement as shown."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "outputs": [],
   "source": [
    "def times(x,y):\n",
    "    '''This multiplies the two input arguments'''\n",
    "    return x*y"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    }
   },
   "outputs": [],
   "source": [
    "c = times(4,5)\n",
    "print(c)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    }
   },
   "source": [
    "Since the **times( )** is now defined, we can document it as shown above. This document is returned whenever **times( )** function is called under [**help( )**](https://docs.python.org/3/library/functions.html#help) function."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "outputs": [],
   "source": [
    "help(times)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "Multiple variable can also be returned as a tuple. However this tends not to be very readable when returning many value, and can easily introduce errors when the order of return values is interpreted incorrectly."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "outputs": [],
   "source": [
    "eglist = [10,50,30,12,6,8,100]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "outputs": [],
   "source": [
    "def egfunc(eglist):\n",
    "    highest = max(eglist)\n",
    "    lowest = min(eglist)\n",
    "    first = eglist[0]\n",
    "    last = eglist[-1]\n",
    "    return highest,lowest,first,last"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "source": [
    "If the function is just called without any variable for it to be assigned to, the result is returned inside a tuple. But if the variables are mentioned then the result is assigned to the variable in a particular order which is declared in the return statement."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "outputs": [],
   "source": [
    "egfunc(eglist)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    }
   },
   "outputs": [],
   "source": [
    "a,b,c,d = egfunc(eglist)\n",
    "print(' a =',a,' b =',b,' c =',c,' d =',d)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "### [Default arguments](https://docs.python.org/3/tutorial/controlflow.html#default-argument-values)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "source": [
    "When an argument of a function is common in majority of the cases this can be specified with a default value. This is also called an implicit argument."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "outputs": [],
   "source": [
    "def implicitadd(x,y=3,z=0):\n",
    "    print(\"%d + %d + %d = %d\"%(x,y,z,x+y+z))\n",
    "    return x+y+z"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "source": [
    "**implicitadd( )** is a function accepts up to three arguments but most of the times the first argument needs to be added just by 3. Hence the second argument is assigned the value 3 and the third argument is zero. Here the last two arguments are default arguments."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "Now if the second argument is not defined when calling the **implicitadd( )** function then it considered as 3."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "outputs": [],
   "source": [
    "implicitadd(4)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    }
   },
   "source": [
    "However we can call the same function with two or three arguments. A useful feature is to explicitly name the argument values being passed into the function. This gives great flexibility in how to call a function with optional arguments. All off the following are valid:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "outputs": [],
   "source": [
    "implicitadd(4,4)\n",
    "implicitadd(4,5,6)\n",
    "implicitadd(4,z=7)\n",
    "implicitadd(2,y=1,z=9)\n",
    "implicitadd(x=1)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "### [Any number of arguments](https://docs.python.org/3/tutorial/controlflow.html#arbitrary-argument-lists)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "source": [
    "If the number of arguments that is to be accepted by a function is not known then a asterisk symbol is used before the name of the argument to hold the remainder of the arguments. The following function requires at least one argument but can have many more."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "outputs": [],
   "source": [
    "def add_n(first,*args):\n",
    "    \"return the sum of one or more numbers\"\n",
    "    reslist = [first] + [value for value in args]\n",
    "    print(reslist)\n",
    "    return sum(reslist)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "The above function defines a list of all of the arguments, prints the list and returns the sum of all of the arguments."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "outputs": [],
   "source": [
    "add_n(1,2,3,4,5)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    }
   },
   "outputs": [],
   "source": [
    "add_n(6.5)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "Arbitrary numbers of named arguments can also be accepted using `**`. When the function is called all of the additional named arguments are provided in a dictionary "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "outputs": [],
   "source": [
    "def namedArgs(**names):\n",
    "    'print the named arguments'\n",
    "    # names is a dictionary of keyword : value\n",
    "    print(\"  \".join(name+\"=\"+str(value) \n",
    "                    for name,value in names.items()))\n",
    "\n",
    "namedArgs(x=3*4,animal='mouse',z=(1+2j))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "###  Global and Local Variables"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "source": [
    "Whatever variable is declared inside a function is local variable and outside the function in global variable."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "outputs": [],
   "source": [
    "eg1 = [1,2,3,4,5]\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    }
   },
   "source": [
    "In the below function we are appending a element to the declared list inside the function. eg2 variable declared inside the function is a local variable."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "outputs": [],
   "source": [
    "def egfunc1():\n",
    "    x=1\n",
    "    def thirdfunc():\n",
    "        x=2\n",
    "        print(\"Inside thirdfunc x =\", x) \n",
    "    thirdfunc()\n",
    "    print(\"Outside x =\", x)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    }
   },
   "outputs": [],
   "source": [
    "egfunc1()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "If a **global** variable is defined as shown in the example below then that variable can be called from anywhere. Global values should be used sparingly as they make functions harder to re-use."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "outputs": [],
   "source": [
    "eg3 = [1,2,3,4,5]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "outputs": [],
   "source": [
    "def egfunc1():\n",
    "    x = 1.0 # local variable for egfunc1\n",
    "    def thirdfunc():\n",
    "        global x # globally defined variable \n",
    "        x = 2.0\n",
    "        print(\"Inside thirdfunc x =\", x) \n",
    "    thirdfunc()\n",
    "    print(\"Outside x =\", x)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    }
   },
   "outputs": [],
   "source": [
    "egfunc1()\n",
    "print(\"Globally defined x =\",x)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "### [Lambda Functions](https://docs.python.org/3/tutorial/controlflow.html#lambda-expressions)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "source": [
    "These are small functions which are not defined with any name and carry a single expression whose result is returned. Lambda functions comes very handy when operating with lists. These function are defined by the keyword **lambda** followed by the variables, a colon and the respective expression."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "outputs": [],
   "source": [
    "z = lambda x: x * x"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    }
   },
   "outputs": [],
   "source": [
    "z(8)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "#### Composing functions"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "source": [
    "Lambda functions can also be used to compose functions"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "outputs": [],
   "source": [
    "def double(x):\n",
    "    return 2*x\n",
    "def square(x):\n",
    "    return x*x\n",
    "def f_of_g(f,g):\n",
    "    \"Compose two functions of a single variable\"\n",
    "    return lambda x: f(g(x))\n",
    "doublesquare= f_of_g(double,square)\n",
    "print(\"doublesquare is a\",type(doublesquare))\n",
    "doublesquare(3)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "## [Classes](https://docs.python.org/3/tutorial/classes.html)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "source": [
    "Variables, Lists, Dictionaries etc in python are objects. Without getting into the theory part of Object Oriented Programming, explanation of the concepts will be done along this tutorial."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    }
   },
   "source": [
    "A class is declared as follows"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    }
   },
   "source": [
    "```python\n",
    "class class_name:\n",
    "    methods (functions)```\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "outputs": [],
   "source": [
    "class FirstClass:\n",
    "    \"This is an empty class\"\n",
    "    pass"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "source": [
    "[**pass**](https://docs.python.org/3/reference/simple_stmts.html#pass) in python means do nothing. The string defines the documentation of the class, accessible via `help(FirstClass)`"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    }
   },
   "source": [
    "Above, a class object named \"FirstClass\" is declared now consider a \"egclass\" which has all the characteristics of \"FirstClass\". So all you have to do is, equate the \"egclass\" to \"FirstClass\". In python jargon this is called as creating an instance. \"egclass\" is the instance of \"FirstClass\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "outputs": [],
   "source": [
    "egclass = FirstClass()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "outputs": [],
   "source": [
    "type(egclass)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "outputs": [],
   "source": [
    "type(FirstClass)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "Objects (instances of a class) can hold data. A variable in an object is also called a field or an attribute. To access a field use the notation `object.field`. For example:x"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "outputs": [],
   "source": [
    "obj1 = FirstClass()\n",
    "obj2 = FirstClass()\n",
    "obj1.x = 5\n",
    "obj2.x = 6\n",
    "x = 7\n",
    "print(\"x in object 1 =\",obj1.x,\"x in object 2=\",obj2.x,\"global x =\",x)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "Now let us add some \"functionality\" to the class.  A function inside a class is called as a \"Method\" of that class"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "outputs": [],
   "source": [
    "class Counter:\n",
    "    def reset(self,init=0):\n",
    "        self.count = init\n",
    "    def getCount(self):\n",
    "        self.count += 1\n",
    "        return self.count\n",
    "counter = Counter()\n",
    "counter.reset(0)\n",
    "print(\"one =\",counter.getCount(),\"two =\",counter.getCount(),\"three =\",counter.getCount())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    }
   },
   "source": [
    "Note that the `reset()` and function and the `getCount()` method are callled with one less argument than they are declared with. The `self` argument is set by Python to the calling object. Here `counter.reset(0)` is equivalent to `Counter.reset(counter,0)`.\n",
    "Using **self** as the name of the first argument of a method is simply a common convention. Python allows any name to be used."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "Note that here it would be better if we could initialise Counter objects immediately with a default value of `count` rather than having to call `reset()`. A constructor method is declared in Python with the special name [`__init__`](https://docs.python.org/3/reference/datamodel.html#object.__init__):"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "outputs": [],
   "source": [
    "class FirstClass:\n",
    "    def __init__(self,name,symbol):\n",
    "        self.name = name\n",
    "        self.symbol = symbol"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    }
   },
   "source": [
    "Now that we have defined a function and added the [\\_\\_init\\_\\_](https://docs.python.org/3/reference/datamodel.html#object.__init__) method. We can create a instance of FirstClass which now accepts two arguments. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "outputs": [],
   "source": [
    "eg1 = FirstClass('one',1)\n",
    "eg2 = FirstClass('two',2)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    }
   },
   "outputs": [],
   "source": [
    "print(eg1.name, eg1.symbol)\n",
    "print(eg2.name, eg2.symbol)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "[**dir( )**](https://docs.python.org/3/library/functions.html#dir) function comes very handy in looking into what the class contains and what all method it offers"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "outputs": [],
   "source": [
    "print(\"Contents of Counter class:\",dir(Counter) )\n",
    "print(\"Contents of counter object:\", dir(counter))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    }
   },
   "source": [
    "[**dir( )**](https://docs.python.org/3/library/functions.html#dir) of an instance also shows it's defined attributes so the object has the additional 'count' attribute. Note that Python defines several default methods for actions like comparison (`__le__` is $\\le$ operator). These and other special methods can be defined for classes to implement specific meanings for how object of that class should be compared, added, multiplied or the like."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "Changing the FirstClass function a bit,"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": true,
    "slideshow": {
     "slide_type": "-"
    }
   },
   "source": [
    "Just like global and local variables as we saw earlier, even classes have it's own types of variables.\n",
    "\n",
    "Class Attribute : attributes defined outside the method and is applicable to all the instances.\n",
    "\n",
    "Instance Attribute : attributes defined inside a method and is applicable to only that method and is unique to each instance."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    }
   },
   "outputs": [],
   "source": [
    "class FirstClass:\n",
    "    test = 'test'\n",
    "    def __init__(self,n,s):\n",
    "        self.name = n\n",
    "        self.symbol = s"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "source": [
    "Here test is a class attribute and name is a instance attribute."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "outputs": [],
   "source": [
    "eg3 = FirstClass('Three',3)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "outputs": [],
   "source": [
    "print(eg3.test,eg3.name,eg3.symbol)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "### [Inheritance](https://docs.python.org/3/tutorial/classes.html#inheritance)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "source": [
    "There might be cases where a new class would have all the previous characteristics of an already defined class. So the new class can \"inherit\" the previous class and add it's own methods to it. This is called as inheritance."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "Consider class SoftwareEngineer which has a method salary."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "outputs": [],
   "source": [
    "class SoftwareEngineer:\n",
    "    def __init__(self,name,age):\n",
    "        self.name = name\n",
    "        self.age = age\n",
    "    def salary(self, value):\n",
    "        self.money = value\n",
    "        print(self.name,\"earns\",self.money)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    }
   },
   "outputs": [],
   "source": [
    "a = SoftwareEngineer('Kartik',26)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "outputs": [],
   "source": [
    "a.salary(40000)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    }
   },
   "outputs": [],
   "source": [
    "[ name for name in dir(SoftwareEngineer) if not name.startswith(\"_\")]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "Now consider another class Artist which tells us about the amount of money an artist earns and his artform."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "outputs": [],
   "source": [
    "class Artist:\n",
    "    def __init__(self,name,age):\n",
    "        self.name = name\n",
    "        self.age = age\n",
    "    def money(self,value):\n",
    "        self.money = value\n",
    "        print(self.name,\"earns\",self.money)\n",
    "    def artform(self, job):\n",
    "        self.job = job\n",
    "        print(self.name,\"is a\", self.job)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "outputs": [],
   "source": [
    "b = Artist('Nitin',20)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "outputs": [],
   "source": [
    "b.money(50000)\n",
    "b.artform('Musician')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    }
   },
   "outputs": [],
   "source": [
    "[ name for name in dir(b) if not name.startswith(\"_\")]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    }
   },
   "source": [
    "money method and salary method are the same. So we can generalize the method to salary and inherit the SoftwareEngineer class to Artist class. Now the artist class becomes,"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "outputs": [],
   "source": [
    "class Artist(SoftwareEngineer):\n",
    "    def artform(self, job):\n",
    "        self.job = job\n",
    "        print (self.name,\"is a\", self.job)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    }
   },
   "outputs": [],
   "source": [
    "c = Artist('Nishanth',21)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "outputs": [],
   "source": [
    "dir(Artist)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "outputs": [],
   "source": [
    "c.salary(60000)\n",
    "c.artform('Dancer')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "Suppose say while inheriting a particular method is not suitable for the new class. One can override this method by defining again that method with the same name inside the new class."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "outputs": [],
   "source": [
    "class Artist(SoftwareEngineer):\n",
    "    def artform(self, job):\n",
    "        self.job = job\n",
    "        print(self.name,\"is a\", self.job)\n",
    "    def salary(self, value):\n",
    "        self.money = value\n",
    "        print(self.name,\"earns\",self.money)\n",
    "        print(\"I am overriding the SoftwareEngineer class's salary method\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    }
   },
   "outputs": [],
   "source": [
    "c = Artist('Nishanth',21)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    }
   },
   "outputs": [],
   "source": [
    "c.salary(60000)\n",
    "c.artform('Dancer')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "If the number of input arguments varies from instance to instance asterisk can be used as shown."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "outputs": [],
   "source": [
    "class NotSure:\n",
    "    def __init__(self, *args):\n",
    "        self.data = ' '.join(list(args)) "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    }
   },
   "outputs": [],
   "source": [
    "yz = NotSure('I', 'Do' , 'Not', 'Know', 'What', 'To','Type')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    }
   },
   "outputs": [],
   "source": [
    "yz.data"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "### Introspection\n",
    "We have already seen the [`dir()`](https://docs.python.org/3/library/functions.html#dir) function for working out what is in a class. Python has many facilities to make introspection easy (that is working out what is in a Python object or module). Some useful functions are [**hasattr**](https://docs.python.org/3/library/functions.html#hasattr), [**getattr**](https://docs.python.org/3/library/functions.html#getattr), and [**setattr**](https://docs.python.org/3/library/functions.html#setattr):"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "-"
    }
   },
   "outputs": [],
   "source": [
    "ns = NotSure('test')\n",
    "if hasattr(ns,'data'): # check if ns.data exists\n",
    "    setattr(ns,'copy', # set ns.copy\n",
    "            getattr(ns,'data')) # get ns.data\n",
    "print('ns.copy =',ns.copy)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.11"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 1
}
